package org.greenstone.gsdl3.service;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.greenstone.gsdl3.util.* ;
import org.w3c.dom.* ;
import org.greenstone.gsdl3.service.KoruGameDatabase.KoruPlayer;
import org.greenstone.gsdl3.service.KoruGameDatabase.KoruTask;

import java.io.*;
import java.net.*;


public class KoruGameServices extends ServiceRack {

	private static final String LOGIN_SERVICE = "Login" ;
	private static final String REGISTER_SERVICE = "Register" ;
	private static final String PLAYER_DETAILS_SERVICE = "GetPlayerDetails" ;
	private static final String SURROGATE_SERVICE = "GetSurrogates" ;
	private static final String DOCUMENT_SERVICE = "GetDocument" ;
	private static final String TASK_SERVICE = "GetTask" ;
	private static final String QUERY_SCORE_SERVICE = "GetQueryScore" ;
	private static final String TASK_SCORE_SERVICE = "PostScore" ;
	private static final String TASK_LIST_SERVICE = "GetTaskList" ;
	private static final String SCOREBOARD_SERVICE = "GetScoreBoard" ;
	private static final String REMINDER_SERVICE = "GetPasswordReminder" ;
	
	private Element taskList ;
	private KoruGameDatabase database ;
	private XMLConverter converter ;
	
	public KoruGameServices() {
		try {
			System.out.println("Creating KoruGameServices") ;
			database = new KoruGameDatabase("wdm", "korugame", "student", "*****", this) ;
			converter = new XMLConverter() ;
			
		} catch (Exception e) {
			e.printStackTrace() ;
		}
	}
	
	
	public void cleanUp() {
		try {
			super.cleanUp();
		} catch (Exception e) {
			System.out.println("KoruGameServices.cleanUp.error") ;
			e.printStackTrace() ;
		}
	}
	
	public boolean configure(Element info, Element extra_info) {
		
		if (!super.configure(info, extra_info)){
			return false;
		}
		
		Element e = this.doc.createElement(GSXML.SERVICE_ELEM);
		e.setAttribute(GSXML.TYPE_ATT, "INVISIBLE");
		e.setAttribute(GSXML.NAME_ATT, PLAYER_DETAILS_SERVICE);
		this.short_service_info.appendChild(e);
		
		e = this.doc.createElement(GSXML.SERVICE_ELEM);
		e.setAttribute(GSXML.TYPE_ATT, "INVISIBLE");
		e.setAttribute(GSXML.NAME_ATT, LOGIN_SERVICE);
		this.short_service_info.appendChild(e);
		
		e = this.doc.createElement(GSXML.SERVICE_ELEM);
		e.setAttribute(GSXML.TYPE_ATT, "INVISIBLE");
		e.setAttribute(GSXML.NAME_ATT, REGISTER_SERVICE);
		this.short_service_info.appendChild(e);
		
		e = this.doc.createElement(GSXML.SERVICE_ELEM);
		e.setAttribute(GSXML.TYPE_ATT, "INVISIBLE");
		e.setAttribute(GSXML.NAME_ATT, SURROGATE_SERVICE);
		this.short_service_info.appendChild(e);
		
		e = this.doc.createElement(GSXML.SERVICE_ELEM);
		e.setAttribute(GSXML.TYPE_ATT, "INVISIBLE");
		e.setAttribute(GSXML.NAME_ATT, DOCUMENT_SERVICE);
		this.short_service_info.appendChild(e);
		
		e = this.doc.createElement(GSXML.SERVICE_ELEM);
		e.setAttribute(GSXML.TYPE_ATT, "INVISIBLE");
		e.setAttribute(GSXML.NAME_ATT, TASK_SERVICE);
		this.short_service_info.appendChild(e);
		
		e = this.doc.createElement(GSXML.SERVICE_ELEM);
		e.setAttribute(GSXML.TYPE_ATT, "INVISIBLE");
		e.setAttribute(GSXML.NAME_ATT, QUERY_SCORE_SERVICE);
		this.short_service_info.appendChild(e);
		
		e = this.doc.createElement(GSXML.SERVICE_ELEM);
		e.setAttribute(GSXML.TYPE_ATT, "INVISIBLE");
		e.setAttribute(GSXML.NAME_ATT, TASK_SCORE_SERVICE);
		this.short_service_info.appendChild(e);
		
		e = this.doc.createElement(GSXML.SERVICE_ELEM);
		e.setAttribute(GSXML.TYPE_ATT, "INVISIBLE");
		e.setAttribute(GSXML.NAME_ATT, TASK_LIST_SERVICE);
		this.short_service_info.appendChild(e);
		
		e = this.doc.createElement(GSXML.SERVICE_ELEM);
		e.setAttribute(GSXML.TYPE_ATT, "INVISIBLE");
		e.setAttribute(GSXML.NAME_ATT, SCOREBOARD_SERVICE);
		this.short_service_info.appendChild(e);
		
		e = this.doc.createElement(GSXML.SERVICE_ELEM);
		e.setAttribute(GSXML.TYPE_ATT, "INVISIBLE");
		e.setAttribute(GSXML.NAME_ATT, REMINDER_SERVICE);
		this.short_service_info.appendChild(e);
		
		return true ;
	}
	
	protected Element getServiceDescription(String service, String lang, String subset) {
		
		if (service.equals(PLAYER_DETAILS_SERVICE)) {
			Element description = this.doc.createElement(GSXML.SERVICE_ELEM);
			description.setAttribute(GSXML.TYPE_ATT, "INVISIBLE");
			description.setAttribute(GSXML.NAME_ATT, PLAYER_DETAILS_SERVICE);
			description.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_NAME,  
					getTextString(PLAYER_DETAILS_SERVICE+".name", lang)));
			description.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_DESCRIPTION,  
					getTextString(PLAYER_DETAILS_SERVICE+".description", lang)));
			return description ;
		}
		
		if (service.equals(LOGIN_SERVICE)) {
			Element description = this.doc.createElement(GSXML.SERVICE_ELEM);
			description.setAttribute(GSXML.TYPE_ATT, "INVISIBLE");
			description.setAttribute(GSXML.NAME_ATT, LOGIN_SERVICE);
			description.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_NAME,  
					getTextString(LOGIN_SERVICE+".name", lang)));
			description.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_DESCRIPTION,  
					getTextString(LOGIN_SERVICE+".description", lang)));
			return description ;
		}
		
		if (service.equals(REGISTER_SERVICE)) {
			Element description = this.doc.createElement(GSXML.SERVICE_ELEM);
			description.setAttribute(GSXML.TYPE_ATT, "INVISIBLE");
			description.setAttribute(GSXML.NAME_ATT, REGISTER_SERVICE);
			description.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_NAME,  
					getTextString(REGISTER_SERVICE+".name", lang)));
			description.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_DESCRIPTION,  
					getTextString(REGISTER_SERVICE+".description", lang)));
			return description ;
		}
		
		if (service.equals(SURROGATE_SERVICE)) {
			Element description = this.doc.createElement(GSXML.SERVICE_ELEM);
			description.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_QUERY);
			description.setAttribute(GSXML.NAME_ATT, SURROGATE_SERVICE);
			description.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_NAME,  
					getTextString(SURROGATE_SERVICE+".name", lang)));
			description.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_DESCRIPTION,  
					getTextString(SURROGATE_SERVICE+".description", lang)));
			return description;
		}

		if (service.equals(DOCUMENT_SERVICE)) {
			Element description = this.doc.createElement(GSXML.SERVICE_ELEM);
			description.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_QUERY);
			description.setAttribute(GSXML.NAME_ATT, DOCUMENT_SERVICE);
			description.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_NAME,  
					getTextString(DOCUMENT_SERVICE+".name", lang)));
			description.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_DESCRIPTION,  
					getTextString(DOCUMENT_SERVICE+".description", lang)));
			return description;
		}
		
		if (service.equals(TASK_SERVICE)) {
			Element description = this.doc.createElement(GSXML.SERVICE_ELEM);
			description.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_QUERY);
			description.setAttribute(GSXML.NAME_ATT, TASK_SERVICE);
			description.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_NAME,  
					getTextString(TASK_SERVICE+".name", lang)));
			description.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_DESCRIPTION,  
					getTextString(TASK_SERVICE+".description", lang)));
			return description;
		}
		
		if (service.equals(QUERY_SCORE_SERVICE)) {
			Element description = this.doc.createElement(GSXML.SERVICE_ELEM);
			description.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_QUERY);
			description.setAttribute(GSXML.NAME_ATT, QUERY_SCORE_SERVICE);
			description.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_NAME,  
					getTextString(QUERY_SCORE_SERVICE+".name", lang)));
			description.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_DESCRIPTION,  
					getTextString(QUERY_SCORE_SERVICE+".description", lang)));
			return description;
		}		
		
		if (service.equals(TASK_SCORE_SERVICE)) {
			Element description = this.doc.createElement(GSXML.SERVICE_ELEM);
			description.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_QUERY);
			description.setAttribute(GSXML.NAME_ATT, TASK_SCORE_SERVICE);
			description.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_NAME,  
					getTextString(TASK_SCORE_SERVICE+".name", lang)));
			description.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_DESCRIPTION,  
					getTextString(TASK_SCORE_SERVICE+".description", lang)));
			return description;
		}
		
		if (service.equals(TASK_LIST_SERVICE)) {
			Element description = this.doc.createElement(GSXML.SERVICE_ELEM);
			description.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_QUERY);
			description.setAttribute(GSXML.NAME_ATT, TASK_LIST_SERVICE);
			description.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_NAME,  
					getTextString(TASK_LIST_SERVICE+".name", lang)));
			description.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_DESCRIPTION,  
					getTextString(TASK_LIST_SERVICE+".description", lang)));
			return description;
		}
		
		if (service.equals(SCOREBOARD_SERVICE)) {
			Element description = this.doc.createElement(GSXML.SERVICE_ELEM);
			description.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_QUERY);
			description.setAttribute(GSXML.NAME_ATT, SCOREBOARD_SERVICE);
			description.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_NAME,  
					getTextString(SCOREBOARD_SERVICE+".name", lang)));
			description.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_DESCRIPTION,  
					getTextString(SCOREBOARD_SERVICE+".description", lang)));
			return description;
		}		
		
		
		
		
		
		return null ;
	}
	
	protected synchronized Element processGetPlayerDetails(Element request) {
		try {
			System.out.println("getting player details") ;
			
			Element param_elem = (Element)GSXML.getChildByTagName(request, GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
			HashMap params = GSXML.extractParams(param_elem, false); 
			String name = (String)params.get("name") ;
			
			KoruPlayer player = database.getPlayer(name) ;
			
			if (player != null) {
				KoruResponse response = new KoruResponse(this.doc, PLAYER_DETAILS_SERVICE) ;
				response.addElement(player.getDescription()) ;
				return response.response ;
			} else {
				KoruResponse response = new KoruResponse(this.doc, PLAYER_DETAILS_SERVICE) ;
				response.addErrorMessage("unknown player") ;
				return response.response ;
			}
			
		} catch (Exception e) {
			System.out.println("KoruGameServices.processGetTaskDetails.error") ;
			e.printStackTrace() ;
			
			KoruResponse r = new KoruResponse(this.doc, PLAYER_DETAILS_SERVICE) ;
			r.addErrorMessage("KoruServices.processGetQueryTopics.error") ;
			return r.response ;
		}	
	}
	
	protected synchronized Element processLogin(Element request) {
		try {
			System.out.println("logging player in") ;
			
			Element param_elem = (Element)GSXML.getChildByTagName(request, GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
			HashMap params = GSXML.extractParams(param_elem, false); 
			String name = (String)params.get("name") ;
			String password = (String)params.get("password") ;
			
			KoruPlayer player = database.getPlayer(name) ;
			
			if (player != null) {
				if (player.getPassword().equals(password)) {
					KoruResponse response = new KoruResponse(this.doc, LOGIN_SERVICE) ;
					response.addElement(doc.createElement("accept")) ;
					return response.response ;
				}
			}
			
			KoruResponse response = new KoruResponse(this.doc, LOGIN_SERVICE) ;
			response.addErrorMessage("invalid username/password") ;
			return response.response ;
			
			
		} catch (Exception e) {
			System.out.println("KoruGameServices.processGetTaskDetails.error") ;
			e.printStackTrace() ;
			
			KoruResponse r = new KoruResponse(this.doc, LOGIN_SERVICE) ;
			r.addErrorMessage("KoruServices.processLogin.error") ;
			return r.response ;
		}	
	}
	
	protected synchronized Element processRegister(Element request) {
		try {
			System.out.println("registering player") ;
			
			Element param_elem = (Element)GSXML.getChildByTagName(request, GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
			HashMap params = GSXML.extractParams(param_elem, false); 
			String name = (String)params.get("name") ;
			String email = (String)params.get("email") ;
			String password = (String)params.get("password") ;
			
			if (database.nameRegistered(name)) {
				KoruResponse response = new KoruResponse(this.doc, REGISTER_SERVICE) ;
				response.addErrorMessage("username unavailable") ;
				return response.response ;
			}
			
			if (database.emailRegistered(email)) {
				KoruResponse response = new KoruResponse(this.doc, REGISTER_SERVICE) ;
				response.addErrorMessage("email is already in use") ;
				return response.response ;	
			}
			
			database.registerPlayer(name, email, password) ;
			KoruResponse response = new KoruResponse(this.doc, REGISTER_SERVICE) ;
			response.addElement(doc.createElement("accept")) ;
			return response.response ;
			
		} catch (Exception e) {
			System.out.println("KoruGameServices.processGetTaskDetails.error") ;
			e.printStackTrace() ;
			
			KoruResponse r = new KoruResponse(this.doc, REGISTER_SERVICE) ;
			r.addErrorMessage("KoruServices.processLogin.error") ;
			return r.response ;
		}	
	}
	
	protected synchronized Element processGetSurrogates(Element request) {
		try {

			//get the parameters
			Element param_elem = (Element)GSXML.getChildByTagName(request, GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
			HashMap params = GSXML.extractParams(param_elem, false);
			
			String playerName = (String)params.get("playerName") ;
			int taskId = Integer.parseInt((String)params.get("taskId")) ;
			
			int wordLimit = Integer.parseInt((String)params.get("wordLimit")) ;
			String[] docHashes = ((String)params.get("docHashes")).split(",") ;	
			Element query = (Element) GSXML.getChildByTagName(request, "query") ; 
	
			Vector terms = getTermsFromQuery(query.getFirstChild().getNodeValue()) ;
			
			//Setup document structure 
			KoruResponse response = new KoruResponse(this.doc, SURROGATE_SERVICE) ;
			
			for (int i=0 ; i < docHashes.length ; i++) { 
				String docText = getDocumentText(request, docHashes[i]) ;
				String docId = getDocumentId(docText) ;
				int relevance = database.getRelevance(taskId, docId) ;
				
				Koru1DocumentSurrogate ds = new Koru1DocumentSurrogate(docText, docHashes[i], terms) ; 

				Element surrogateXML = doc.createElement("DocumentSurrogate") ;
				surrogateXML.setAttribute("relevance", new Integer(relevance).toString()) ;
				surrogateXML.setAttribute("hash", docHashes[i]) ;
				surrogateXML.setAttribute("id", docId) ;
				surrogateXML.setAttribute("numTerms", new Integer(terms.size()).toString()) ;
				
				surrogateXML.appendChild(ds.getSnippetXML(this.doc, wordLimit)) ;
				//surrogateXML.appendChild(ds.getMatchListXML(this.doc)) ;

				response.addElement(surrogateXML) ;
			} 

			return response.response ;	    
		} catch (Exception e) { 
			System.out.println("Koru1Services.processGetSurrogates.error") ; 
			e.printStackTrace() ; 

			KoruResponse r = new KoruResponse(this.doc, SURROGATE_SERVICE) ; 
			r.addErrorMessage("Koru1Services.processGetSurrogates.error") ; 
			return r.response ; 
		}	     
	}
	
	protected synchronized Element processGetDocument(Element request) {
		try {
			// get the parameters

			Element param_elem = (Element)GSXML.getChildByTagName(request, GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
			HashMap params = GSXML.extractParams(param_elem, false);

			String docHash = (String) params.get("docHash") ;
			int taskId = Integer.parseInt((String)params.get("taskId")) ;
			Element query = (Element) GSXML.getChildByTagName(request, "query") ; 
			
			Vector terms = getTermsFromQuery(query.getChildNodes().item(0).getNodeValue()) ;
			
			String docText = getDocumentText(request, docHash) ;
			String docId = getDocumentId(docText) ;
			int relevance = database.getRelevance(taskId, docId) ;

			Koru1DocumentSurrogate docSurrogate = new Koru1DocumentSurrogate(docText, docHash, terms) ;
			KoruResponse response = new KoruResponse(this.doc, DOCUMENT_SERVICE) ;
			
			Element documentXML = docSurrogate.getDocumentXML(this.doc) ;
			documentXML.setAttribute("numTerms", new Integer(terms.size()).toString()) ;
			documentXML.setAttribute("id", docId) ;
			documentXML.setAttribute("relevance", String.valueOf(relevance)) ;
			
			response.addElement(documentXML) ;
			//response.addElement(docSurrogate.getMatchListXML(this.doc)) ;

			return response.response ;

		} catch (Exception e) {
			System.out.println("ImprovedPhindTopicBrowse.processGetDocument.error") ;
			e.printStackTrace() ;
			KoruResponse r = new KoruResponse(this.doc, DOCUMENT_SERVICE) ;
			r.addErrorMessage("ImprovedPhindTopicBrowse.processGetDocument.error") ;
			return r.response ;
		}
	}
	
	protected synchronized Element processGetTask(Element request) {
		try {
			Element param_elem = (Element)GSXML.getChildByTagName(request, GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
			HashMap params = GSXML.extractParams(param_elem, false); 
			String playerName = (String)params.get("player") ;
			int taskId = Integer.parseInt((String)params.get("taskId")) ;
			
			KoruTask task ;
			
			
			task = database.getTask(taskId) ;
			Element taskXml = task.getXml() ;
				
			if (playerName != null && playerName != "") {
				KoruPlayer player = database.getPlayer(playerName) ;

				taskXml.appendChild(player.getTaskScore(taskId)) ;
				taskXml.appendChild(player.getBestQueries(taskId)) ;
				taskXml.appendChild(player.getRecentQueries(taskId)) ;
			}
			
			KoruResponse response = new KoruResponse(this.doc, TASK_SERVICE) ;
			response.addElement(taskXml) ;  
			return response.response ;
			
		} catch (Exception e) {
			System.out.println("KoruGameServices.processGetTask.error") ;
			e.printStackTrace() ;
			
			KoruResponse r = new KoruResponse(this.doc, TASK_SERVICE) ;
			r.addErrorMessage("KoruServices.processGetTask.error") ;
			return r.response ;
		}
	}
	
	
	
	private Vector getTermsFromQuery(String query) {
		Vector terms = new Vector() ;

		String queryText = " " + query + " " ;
		
		queryText = queryText.replace("(", " ") ;
		queryText = queryText.replace(")", " ") ;
		queryText = queryText.replace('\"', '^') ;
		queryText = queryText.replace('\'', '^') ;
		String unquotedText = "" ;
		
		Pattern p = Pattern.compile(" \\^[^\\^]*\\^ ") ;
		Matcher m = p.matcher(queryText) ;
		
		int lastCopyPoint = 0 ;

		while (m.find()) {
			int pos = m.start() + 1 ;
			
			String quotedTerm = queryText.substring(m.start() + 2, m.end() - 2) ;
				
			if (!terms.contains(quotedTerm))
				terms.add(quotedTerm) ;
			
			unquotedText = unquotedText + queryText.substring(lastCopyPoint, pos) ;			
			lastCopyPoint = pos + quotedTerm.length() + 2;
		}
		
		unquotedText = unquotedText + queryText.substring(lastCopyPoint) ;
		String[] unquotedTerms = unquotedText.split(" ") ;
		for(int i=0;i<unquotedTerms.length;i++) {
			String unquotedTerm = unquotedTerms[i].trim() ;
			if (!unquotedTerm.equals("") && !terms.contains(unquotedTerm) && !unquotedTerm.equalsIgnoreCase("and") && !unquotedTerm.equalsIgnoreCase("or") && !unquotedTerm.equalsIgnoreCase("not"))
				terms.add(unquotedTerm) ;
		}
		
		return terms ;
	}
	
	private String getDocumentText(Element request, String docHash) throws Exception {
		Element docContentRequest = createDocumentTextRequest(request, docHash) ;
		Element docContentResponse = (Element)this.router.process(docContentRequest) ;
		
		if (docContentResponse == null) {
			System.out.println("No response recieved for document content request") ;
			throw new Exception("No response recieved for document content request") ;
		}
		
		String documentText = "" ;
		NodeList nodeContentList = docContentResponse.getElementsByTagName("nodeContent") ;
		for (int i=0 ; i < nodeContentList.getLength() ; i++) {
			Element nodeContent = (Element) nodeContentList.item(i) ;
			documentText = documentText +  nodeContent.getFirstChild().getNodeValue() ;
		}
		
		System.out.println("DocumentText: " + documentText.length()) ;
		return documentText ;
	}
	
	
	private Element createDocumentTextRequest(Element oldRequest, String docHash) {
		try {
			Element newRequest = doc.createElement(GSXML.REQUEST_ELEM) ;
			newRequest.setAttribute(GSXML.LANG_ATT, oldRequest.getAttribute(GSXML.LANG_ATT)) ;
			newRequest.setAttribute(GSXML.TO_ATT, this.cluster_name + "/DocumentContentRetrieve") ;
			newRequest.setAttribute(GSXML.TYPE_ATT, oldRequest.getAttribute(GSXML.TYPE_ATT)) ;
			newRequest.setAttribute(GSXML.USER_ID_ATT, oldRequest.getAttribute(GSXML.USER_ID_ATT)) ;
			
			Element paramList = doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER) ;
			newRequest.appendChild(paramList) ;
			
			Element param = doc.createElement(GSXML.PARAM_ELEM) ;
			param.setAttribute(GSXML.NAME_ATT, "ext") ;
			param.setAttribute(GSXML.VALUE_ATT, "0") ;
			paramList.appendChild(param) ;
			
			Element docNodeList = doc.createElement(GSXML.DOC_NODE_ELEM + GSXML.LIST_MODIFIER) ;
			newRequest.appendChild(docNodeList) ;
			
			Element docNode = doc.createElement(GSXML.DOC_NODE_ELEM) ;
			docNode.setAttribute(GSXML.NODE_ID_ATT, docHash) ;
			docNodeList.appendChild(docNode) ;
			
			Element message = doc.createElement(GSXML.MESSAGE_ELEM) ;
			message.appendChild(newRequest) ;
			
			//GSXML.printXMLNode(message, 0) ;
			return message ;
		} catch (Exception e) {
			System.out.println("Koru1Services.createDocumentRetrieve.error") ;
			e.printStackTrace() ;
			return null ;
		}
	}
	
	protected synchronized Element processGetQueryScore(Element request) {
		try {

			//get the parameters
			Element param_elem = (Element)GSXML.getChildByTagName(request, GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
			HashMap params = GSXML.extractParams(param_elem, false);
			
			String playerName = (String)params.get("playerName") ;
			int taskId = Integer.parseInt((String)params.get("taskId")) ;
			
			String[] docHashes = ((String)params.get("docHashes")).split(",") ;	
			Element query = (Element) GSXML.getChildByTagName(request, "query") ; 
			String queryText = query.getFirstChild().getNodeValue() ;
	
			Vector terms = getTermsFromQuery(queryText) ;
			
			//Setup document structure 
			KoruResponse response = new KoruResponse(this.doc, QUERY_SCORE_SERVICE) ;
			
			int docsFound = 0 ;
			int docsFound_firstPage = 0 ;
			
			for (int i=0 ; i < docHashes.length ; i++) { 
				String docText = getDocumentText(request, docHashes[i]) ;
				String docId = getDocumentId(docText) ;
				int relevance = database.getRelevance(taskId, docId) ;
				
				if (relevance == 2) {
					
					docsFound++ ;
					
					if (i < 10)
						docsFound_firstPage ++ ; 
				}
			} 
			
			int totalDocs = database.getRelevantDocsForTask(taskId) ;
			
			double recall = (double)docsFound / totalDocs ;
			double precision = (double)docsFound / docHashes.length ;
			
			double recall_firstPage = (double)docsFound_firstPage / totalDocs ;
			double precision_firstPage = (double)docsFound_firstPage / Math.min(10, docHashes.length) ;
			
			double alpha = 1 ;
			double fMeasure = ((1 + alpha) * precision * recall) / ((alpha * precision) + recall) ;
			
			int totalScore = (int)Math.round(1000*(fMeasure)) ;

			//int totalScore = (int)Math.round(250 * recall + precision + recall_firstPage + precision_firstPage) ;
			
			database.postQuery(playerName, taskId, queryText, recall, precision, recall_firstPage, precision_firstPage, totalScore, docHashes.length, docsFound, totalDocs) ;
			
			KoruTask task = database.getTask(taskId) ;
			String ranking = database.getRankAsString(task.getRanking(playerName)) ;
			
			Element queryScore = doc.createElement("queryScore") ;
			queryScore.setAttribute("score", String.valueOf(totalScore)) ;
			queryScore.setAttribute("recall", String.valueOf(recall)) ;
			queryScore.setAttribute("precision", String.valueOf(precision)) ;
			queryScore.setAttribute("recall_firstPage", String.valueOf(recall_firstPage)) ;
			queryScore.setAttribute("precision_firstPage", String.valueOf(precision_firstPage)) ;
			queryScore.setAttribute("ranking", ranking) ;

			queryScore.setAttribute("docs_returned", String.valueOf(docHashes.length)) ;
			queryScore.setAttribute("rel_docs_returned", String.valueOf(docsFound)) ;
			queryScore.setAttribute("rel_docs_total", String.valueOf(totalDocs)) ;
			
			
			KoruPlayer player = database.getPlayer(playerName) ;
				
			queryScore.appendChild(player.getBestQueries(taskId)) ;
			queryScore.appendChild(player.getRecentQueries(taskId)) ;
			
			
			
			response.addElement(queryScore) ;
			
			return response.response ;	    
		} catch (Exception e) { 
			System.out.println("Koru1Services.processGetQueryScore.error") ; 
			e.printStackTrace() ; 

			KoruResponse r = new KoruResponse(this.doc, QUERY_SCORE_SERVICE) ; 
			r.addErrorMessage("Koru1Services.processGetQueryScore.error") ; 
			return r.response ; 
		}	     
	}
		
	protected synchronized Element processPostScore(Element request) {
		try {

			//get the parameters
			Element param_elem = (Element)GSXML.getChildByTagName(request, GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
			HashMap params = GSXML.extractParams(param_elem, false);
			
			String playerName = (String)params.get("player") ;
			int taskId = Integer.parseInt((String)params.get("taskId")) ;
			int score = Integer.parseInt((String)params.get("taskId")) ;
			
			String[] docsLocated = ((String)params.get("docsLocated")).split(",") ;	
			
			database.postTaskScore(playerName, taskId, score) ;
			
						
			//Setup document structure 
			KoruResponse response = new KoruResponse(this.doc, TASK_SCORE_SERVICE) ;		
			return response.response ;	    
		} catch (Exception e) { 
			System.out.println("Koru1Services.processGetQueryScore.error") ; 
			e.printStackTrace() ; 

			KoruResponse r = new KoruResponse(this.doc, TASK_SCORE_SERVICE) ; 
			r.addErrorMessage("Koru1Services.processGetQueryScore.error") ; 
			return r.response ; 
		}	     
	}
	
	
	
	private String getDocumentId(String docText) {
		String docId = null ;
		
		//System.out.println(docText) ;
		
		int pos1 = docText.indexOf("meta") ;
		pos1 = docText.indexOf("content=") ;
		pos1 = pos1 + 9 ;
		int pos2 = docText.indexOf("\" ", pos1) ;
		
		String id = docText.substring(pos1, pos2) ;
		//System.out.println("id=" + id) ;
		return id ;
	}
	
	
	protected synchronized Element processGetTaskList(Element request) {
		try {
			Element param_elem = (Element)GSXML.getChildByTagName(request, GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
			HashMap params = GSXML.extractParams(param_elem, false); 
			String playerName = (String)params.get("player") ;	
			
			Vector<KoruTask> tasks = database.getAllTasks() ;
			Element taskList = doc.createElement("taskList") ;
			
			Iterator<KoruTask> i = tasks.iterator() ;
			while (i.hasNext()) {
				KoruTask task = i.next() ;
				
				String ranking = database.getRankAsString(task.getRanking(playerName)) ;
				
				Element taskXml = task.getXml() ;
				taskXml.setAttribute("ranking", ranking) ;
				taskList.appendChild(taskXml) ;
			}
			
			KoruResponse response = new KoruResponse(this.doc, TASK_LIST_SERVICE) ;
			response.addElement(taskList) ;  
			return response.response ;
			
		} catch (Exception e) {
			System.out.println("KoruGameServices.processGetTaskList.error") ;
			e.printStackTrace() ;
			
			KoruResponse r = new KoruResponse(this.doc, TASK_LIST_SERVICE) ;
			r.addErrorMessage("KoruServices.processGetTaskList.error") ;
			return r.response ;
		}
	}
	
	protected synchronized Element processGetScoreBoard(Element request) {
		try {
			Element param_elem = (Element)GSXML.getChildByTagName(request, GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
			HashMap params = GSXML.extractParams(param_elem, false); 
			String playerName = (String)params.get("player") ;	
			
			Element topPlayers = doc.createElement("topPlayersList") ;
			
			Iterator<KoruPlayer> i = database.getTopPlayers().iterator() ;
			while (i.hasNext()) {
				KoruPlayer player = i.next() ;
				topPlayers.appendChild(player.getDescription()) ;
			}
					
			KoruResponse response = new KoruResponse(this.doc, TASK_LIST_SERVICE) ;	
			response.addElement(topPlayers) ;
			
			Vector<KoruPlayer> neighbors = database.getNeighbors(database.getPlayer(playerName)) ;
			
			if (neighbors != null) {
				Element neighboringPlayers = doc.createElement("neighboringPlayers") ;
				
				i = neighbors.iterator() ;
				while (i.hasNext()) {
					KoruPlayer player = i.next() ;
					neighboringPlayers.appendChild(player.getDescription()) ;
				}
			}
			
			return response.response ;
			
		} catch (Exception e) {
			System.out.println("KoruGameServices.processGetTaskList.error") ;
			e.printStackTrace() ;
			
			KoruResponse r = new KoruResponse(this.doc, TASK_LIST_SERVICE) ;
			r.addErrorMessage("KoruServices.processGetTaskList.error") ;
			return r.response ;
		}
	}
	
	protected synchronized Element processGetPasswordReminder(Element request) {
		try {
			Element param_elem = (Element)GSXML.getChildByTagName(request, GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
			HashMap params = GSXML.extractParams(param_elem, false); 
			String playerName = (String)params.get("player") ;	
			
			KoruResponse response = new KoruResponse(this.doc, REMINDER_SERVICE) ;	
			
			//KoruPlayer player = database.getPlayer(playerName) ;

			// get a socket connection to the mail
			// server at SMTP port 25
			Socket socket = new Socket("130.217.241.37", 25);

			// Create an output stream for sending message
			PrintWriter out = new PrintWriter(socket.getOutputStream(), true);

			// send mail using SMTP protocol
			out.println("MAIL FROM: dnk2@cs.waikato.ac.nz");
			out.println("RCPT TO: d.n.milne@gmail.com");
			out.println("DATA\n");  // Skip line after DATA
			out.println("Test message ");
			out.println(".");       // End message with a single period
			out.flush();

			return response.response ;
			
		} catch (Exception e) {
			System.out.println("KoruGameServices.processGetTaskList.error") ;
			e.printStackTrace() ;
			
			KoruResponse r = new KoruResponse(this.doc, TASK_LIST_SERVICE) ;
			r.addErrorMessage("KoruServices.processGetTaskList.error " + e) ;
			return r.response ;
		}
	}
}
